package edu.ccut.saturn.component;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.osgi.framework.Bundle;

import edu.ccut.saturn.component.utils.ISaturnCache;
import edu.ccut.saturn.component.utils.ISaturnMemCache;
import edu.ccut.saturn.component.utils.SaturnMemCacheManager;
import edu.ccut.saturn.component.utils.cache.SaturnMapCache;
import edu.ccut.saturn.core.IConstants;
import edu.ccut.saturn.core.impl.HostActivator;
import edu.ccut.saturn.core.impl.SaturnBundleService;
import edu.ccut.saturn.core.impl.SaturnPropertiesService;

/**
 * 组件模型的核心类。
 * <p>
 * 提供了一个单态的实例，负责管理维护所有加载的工程中的action、logic、component组件信息及组件的运行期实例，在saturn平台中组件运行期实例都是单态的。
 * 通过组件所在的bundle名称和组件的id组成的全局键值来唯一标识一个组件。 提供了通过组件全局标识获得组件信息<code>IComponentInfo</code>及运行期组件实例<code>IComponent</code>的方法。
 * <p>
 * <li>字段components：记录了以组件的全局键值标识的组件运行实例；
 * <li>字段bundleComponentInfos：记录了以bundle名称标识的每个bundle范围内的组件信息的集合。
 * 
 * @author 谷钰
 * @version 1.0 2008.9.05
 */
public class ComponentManager {

	private static final String LOGGER_KEY_BUNDLE_SERVICE = SaturnLoggerManager.LOGGER_KEY_BUNDLE_SERVICE;

	protected static ILogger logger = SaturnLoggerManager.getInstance()
			.getSaturnLogger(LOGGER_KEY_BUNDLE_SERVICE);

	private static final String KEY_SEPARATOR = "::";

	private ISaturnCache bundleComponentInfos;

	private ISaturnCache components;
	
	private static List<IComponentListener> listeners = new ArrayList<IComponentListener>();

	private static ComponentManager instance;

	static {
		instance = new ComponentManager();
	}

	private ComponentManager() {
		//TODO 选择不同的缓存方式
		//ISaturnMemCache cache = SaturnMemCacheManager.getInstance().getSaturnCache(SaturnMemCacheManager.DEFAULTCLIENT);
		ISaturnCache cache = new SaturnMapCache();
		this.bundleComponentInfos = cache;
		this.components = cache;
	}

	/**
	 * 获得类的单态实例
	 * 
	 * @return
	 */
	public static ComponentManager getInstance() {
		return instance;
	}

	/**
	 * 添加bundle内部加载的action组件、logic组件和component组件信息，以bundle的名称为键值保存这些组件信息。
	 * 
	 * @param bundleInfo
	 */
	public void addBundleComponentInfo(IBundleInfo bundleInfo) {
		this.bundleComponentInfos.put(bundleInfo.getBundleSymbolicName(),
				bundleInfo.getBundleComopnentInfo());

		logger.debug("add bundleComponentInfo key \""
				+ bundleInfo.getBundleSymbolicName() + "\"");
	}

	/**
	 * 根据传入的bundle名称，删除组件信息和组件运行时实例。
	 * 
	 * @param bundleSymbolicName
	 */
	public void removeBundleComponentInfo(String bundleSymbolicName) {
		if (this.bundleComponentInfos.containsKey(bundleSymbolicName)) {
			IBundleComponentInfo bundleComponentInfo = (IBundleComponentInfo)this.bundleComponentInfos
					.get(bundleSymbolicName);

			for (String componentKey : bundleComponentInfo.getComponentKeys()) {
				this.removeComponent(bundleSymbolicName, componentKey);
			}

			this.bundleComponentInfos.remove(bundleSymbolicName);
			logger.debug("remove bundleComponentInfo key is : \""
					+ bundleComponentInfo.getBundleSymbolicName() + "\"");

			return;
		}

		logger
				.warn("failed to remove bundleComponentInfo, because the bundle key : \""
						+ bundleSymbolicName + "\" is not valid");
	}

	/**
	 * 通过bundle名获得一个已加载的bundle的组件信息。
	 * 
	 * @param bundleSymbolicName
	 * @return 如果参数bundleSymbolicName指向了一个没有被加载过的bundle对象，则返回null
	 */
	public IBundleComponentInfo getBundleComponentInfo(String bundleSymbolicName) {
		return (IBundleComponentInfo)this.bundleComponentInfos.get(bundleSymbolicName);
	}

	/**
	 * 获得已经加载的所有的bundle对象的名称。
	 * 
	 * @return
	 */
	public Set<String> getBundleSymbolicNames() {
		return this.bundleComponentInfos.keySet();
	}

	/**
	 * 根据组件的全局键值获得组件的运行时实例
	 * 
	 * @param componentFullKey
	 *            bundleSymbolicName + "::" + componentKey
	 * @return
	 * @throws SaturnComponentException
	 */
	public IComponent getComponent(String componentFullKey)
			throws SaturnComponentException {

		int index = componentFullKey.indexOf(KEY_SEPARATOR);

		if (index == -1 || index == 0 || index == componentFullKey.length() - 2) {
			throw new SaturnComponentException(componentFullKey
					+ " is not a valid key");
		}

		String bundleSymbolicName = componentFullKey.substring(0, index);
		String componentKey = componentFullKey.substring(index + 2);

		return this.getComponent(bundleSymbolicName, componentKey);
	}

	/**
	 * 通过bundle名和组件键值获得运行时的组件实例。
	 * 首先去现有的实例对象中查找，如果已有生成的组件运行时实例对象则返回这个对象，否则根据相应的组件信息创建出这个组件的运行时实例。
	 * 
	 * @param bundleSymbolicName
	 * @param componentKey
	 * @return
	 * @throws SaturnComponentException
	 */
	public IComponent getComponent(String bundleSymbolicName,
			String componentKey) throws SaturnComponentException {

		String componentFullKey = this.getComponentFullKey(bundleSymbolicName,
				componentKey);

		if (this.components.containsKey(componentFullKey)) {
			return (IComponent)this.components.get(componentFullKey);
		}

		IComponentInfo componentInfo = this.getComponentInfo(
				bundleSymbolicName, componentKey);

		if (componentInfo == null) {
			throw new SaturnComponentException(componentFullKey
					+ " dose not exist! ");
		}
		IComponent component = componentInfo.createComponent();
		this.components.put(componentFullKey, component);

		return component;
	}

	/**
	 * 通过组件的全局键值来获得组件信息<code>IComponentInfo</code>
	 * 
	 * @param componentFullKey
	 * @return
	 * @throws SaturnComponentException
	 */
	public IComponentInfo getComponentInfo(String componentFullKey)
			throws SaturnComponentException {

		int index = componentFullKey.indexOf(KEY_SEPARATOR);

		if (index == -1 || index == 0 || index == componentFullKey.length() - 2) {
			throw new SaturnComponentException(componentFullKey
					+ " is not a valid key");
		}

		String bundleSymbolicName = componentFullKey.substring(0, index);
		String componentKey = componentFullKey.substring(index + 2);

		return this.getComponentInfo(bundleSymbolicName, componentKey);
	}

	/**
	 * 通过bundle名称和组件id获得组件信息
	 * 
	 * @param bundleSymbolicName
	 * @param componentKey
	 * @return
	 * @throws SaturnComponentException
	 */
	public IComponentInfo getComponentInfo(String bundleSymbolicName,
			String componentKey) throws SaturnComponentException {

		IBundleComponentInfo bundleComponentInfo = (IBundleComponentInfo)this.bundleComponentInfos
				.get(bundleSymbolicName);

		if (bundleComponentInfo == null) {
			throw new SaturnComponentException(
					"can't find bundle with symbolicName = ["
							+ bundleSymbolicName + "]");
		}

		IComponentInfo componentInfo = bundleComponentInfo
				.getComponentInfo(componentKey);

		if ("true".equals(SaturnPropertiesService.getInstance().getProperty(
				IConstants.SATURN_COMPONENTINFO_LOADER_INSTRUCTION))) {

			if (componentInfo == null) {
				componentInfo = getComponentInfo(bundleSymbolicName,
						componentKey, bundleComponentInfo);
			}
		}
		return componentInfo;
	}

	private IComponentInfo getComponentInfo(String bundleSymbolicName,
			String componentKey, IBundleComponentInfo bundleComponentInfo)
			throws SaturnComponentException {

		Bundle bundle = SaturnBundleService.getInstance().getBundle(bundleSymbolicName);

		if (bundle == null || bundleComponentInfo == null) {
			throw new SaturnComponentException();

		} else {
			IComponentInfo componentInfo = ComponentFactory
					.getComponentInfoByKey(bundle, componentKey);

			bundleComponentInfo.addComponentInfo(componentInfo);
			return componentInfo;
		}
	}

	private void removeComponent(String bundleSymbolicName, String componentKey) {
		String componentFullKey = this.getComponentFullKey(bundleSymbolicName,
				componentKey);

		if (this.components.containsKey(componentFullKey)) {
			this.components.remove(componentFullKey);
		} else {	
		    logger.warn("failed to remove componentInfo, because the component key : \""
						+ componentFullKey + "\" is not valid");
		}
	}

	private String getComponentFullKey(String bundleSymbolicName,
			String componentKey) {

		return bundleSymbolicName + KEY_SEPARATOR + componentKey;
	}
	
	public static void addComponentListener(IComponentListener componentListener) {
		listeners.add(componentListener);
	}
	
	public static void removeComponentListener(IComponentListener componentListener) {
		listeners.remove(componentListener);
	}
	
	public static void beforeExecuteComponent(INodeInfo nodeInfo, IParameterContext parameterContext) {
		for (IComponentListener listener : listeners) {
			listener.beforeExecute(nodeInfo, parameterContext);
		}
	}
	
	public static void afterExecuteComponent(INodeInfo nodeInfo, IParameterContext parameterContext) {
		for (IComponentListener listener : listeners) {
			listener.afterExecute(nodeInfo, parameterContext);
		}
	}
}
